﻿using AutoMapper;
using HelenServer.BugEngine.Contracts;
using HelenServer.Migrator;
using Microsoft.EntityFrameworkCore;

namespace HelenServer.BugEngine.Dal;

public class BugEngineMigrateProfile : Profile
{
    protected internal static ZentaoDbContext ZtContext => MigrateProvider.Context ?? throw new InvalidOperationException("请先调用MigrateProvider.ReadyToMigrate方法");

    protected internal static BugEngineDbContext BgeContext
        => new Lazy<BugEngineDbContext>(() =>
        {
            var config = Helen.Services.GetRequiredService<IConfiguration>();
            var connectionString = config.GetConnectionString("master");

            return new BugEngineDbContext(new DbContextOptionsBuilder<BugEngineDbContext>().UseSqlServer(connectionString).Options);
        }).Value;

    public BugEngineMigrateProfile()
    {
        this.CreateMap<ZtProduct, Product>()
            .ForMember(dest => dest.Status, member => member.MapFrom(source => Helen.ToEnum<ProductStatus>(source.Status)))
            .ForMember(dest => dest.Description, member => member.MapFrom(source => source.Desc))
            .ForMember(dest => dest.ProductOwner, member => member.MapFrom(source => MapVirtualUser(source.Po)))
            .ForMember(dest => dest.TestLeader, member => member.MapFrom(source => MapVirtualUser(source.Qd)))
            .ForMember(dest => dest.ReleaseManager, member => member.MapFrom(source => MapVirtualUser(source.Rd)))
            .ForMember(dest => dest.CreatedBy, member => member.MapFrom(source => MapVirtualUser(source.CreatedBy)))
            .ForMember(dest => dest.Deleted, member => member.MapFrom(source => source.Deleted == "1"));

        this.CreateMap<ZtModule, Module>()
            .ForMember(dest => dest.Product, member => member.MapFrom(source => MapVirtualProduct((int)source.Root)))
            .ForMember(dest => dest.Parent, member => member.MapFrom(source => MapVirtualProduct((int)source.Root)))
            .ForMember(dest => dest.Deleted, member => member.MapFrom(source => source.Deleted == "1"));

        this.CreateMap<ZtRelease, Release>()
            .ForMember(dest => dest.Product, member => member.MapFrom(source => MapVirtualProduct((int)source.Product)))
            .ForMember(dest => dest.Description, member => member.MapFrom(source => source.Desc))
            .ForMember(dest => dest.CreatedDate, member => member.MapFrom(source => MapDateOnly(source.Date)))
            .ForMember(dest => dest.Status, member => member.MapFrom(source => Helen.ToEnum<ReleaseStatus>(source.Status)))
            .ForMember(dest => dest.Deleted, member => member.MapFrom(source => source.Deleted == "1"));

        this.CreateMap<ZtBug, Bug>()
            .ForMember(dest => dest.Product, member => member.MapFrom(source => MapVirtualProduct((int)source.Product)))
            .ForMember(dest => dest.Module, member => member.MapFrom(source => MapVirtualModule((int)source.Module)))
            .ForMember(dest => dest.Demand, member => member.MapFrom(source => MapVirtualDemand(source.ToStory)))
            .ForMember(dest => dest.Priority, member => member.MapFrom(source => source.Pri))
            .ForMember(dest => dest.Type, member => member.MapFrom(source => MapVirtualBugType(source.Type)))
            .ForMember(dest => dest.Os, member => member.MapFrom(source => MapVirtualOs(source.Os)))
            .ForMember(dest => dest.Browser, member => member.MapFrom(source => MapVirtualBrowser(source.Browser)))
            .ForMember(dest => dest.Status, member => member.MapFrom(source => Helen.ToEnum<BugStatus>(source.Status)))
            .ForMember(dest => dest.LastActivatedDate, member => member.MapFrom(source => MapDateTime(source.ActivatedDate)))
            .ForMember(dest => dest.Cclist, member => member.MapFrom(source => MapVirtualUsers(source.Mailto)))
            .ForMember(dest => dest.Creator, member => member.MapFrom(source => MapVirtualUser(source.OpenedBy)))
            .ForMember(dest => dest.CreatedDate, member => member.MapFrom(source => MapDateTime(source.OpenedDate)))
            .ForMember(dest => dest.AffectVersion, member => member.MapFrom(source => MapVirtualReleases(source.OpenedBuild)))
            .ForMember(dest => dest.AssignedTo, member => member.MapFrom(source => MapVirtualUser(source.AssignedTo)))
            .ForMember(dest => dest.AssignedDate, member => member.MapFrom(source => MapDateTime(source.AssignedDate)))
            .ForMember(dest => dest.Deadline, member => member.MapFrom(source => MapDateOnly(source.Deadline)))
            .ForMember(dest => dest.ResolvedBy, member => member.MapFrom(source => MapVirtualUser(source.ResolvedBy)))
            .ForMember(dest => dest.Resolution, member => member.MapFrom(source => MapVirtualSolution(source.Resolution)))
            .ForMember(dest => dest.ResolvedVersion, member => member.MapFrom(source => MapVirtualRelease(source.ResolvedBuild)))
            .ForMember(dest => dest.ResolvedDate, member => member.MapFrom(source => MapDateTime(source.ResolvedDate)))
            .ForMember(dest => dest.ClosedBy, member => member.MapFrom(source => MapVirtualUser(source.ClosedBy)))
            .ForMember(dest => dest.ClosedDate, member => member.MapFrom(source => MapDateTime(source.ClosedDate)))
            .ForMember(dest => dest.DuplicateBug, member => member.MapFrom(source => MapVirtualBug((int)source.DuplicateBug)))
            .ForMember(dest => dest.LinkBugs, member => member.MapFrom(source => MapVirtualBugs(source.LinkBug)))
            .ForMember(dest => dest.LastEditedBy, member => member.MapFrom(source => MapVirtualUser(source.LastEditedBy)))
            .ForMember(dest => dest.LastEditedDate, member => member.MapFrom(source => MapDateTime(source.LastEditedDate)))
            .ForMember(dest => dest.Deleted, member => member.MapFrom(source => source.Deleted == "1"));

        this.CreateMap<ZtAction, OperationLog>()
            .ForMember(dest => dest.Operation, member => member.MapFrom(source => MapVirtualOperation(source.Action)))
            .ForMember(dest => dest.Extra, member => member.MapFrom(source => MapVirtualExtra(source.Extra)))
            .ForMember(dest => dest.Operator, member => member.MapFrom(source => MapVirtualUser(source.Actor)))
            .ForMember(dest => dest.OperatedDate, member => member.MapFrom(source => MapDateTime(source.Date)))
            .ForMember(dest => dest.Remark, member => member.MapFrom(source => source.Comment));

        this.CreateMap<ZtHistory, OperationLogDetail>()
            .ForMember(dest => dest.Field, member => member.MapFrom(source => MapVirtualField(source.Field)))
            .ForMember(dest => dest.Old, member => member.MapFrom(source => MapVirtualRecord(source, true)))
            .ForMember(dest => dest.New, member => member.MapFrom(source => MapVirtualRecord(source, false)));
    }


    private static DateTime? MapDateOnly(DateOnly date)
    {
        return DateTime.TryParse(date.ToString(), out var time)
            ? time > Helen.MinimumDate ? time : null : null;
    }

    private static DateTime? MapDateTime(DateTime date)
    {
        return DateTime.TryParse(date.ToString(), out var time)
            ? time > Helen.MinimumDate ? time : Helen.MinimumDate : null;
    }

    private static int? MapVirtualUser(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return null;
        }

        var entity = MigrateProvider.DbCache.ZtUsers.FirstOrDefault(n => n.Account == value);

        return entity is not null ? (int)entity.Id : null;
    }

    private static int[] MapVirtualUsers(string? value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return Array.Empty<int>();
        }

        var users = value.Split(',', StringSplitOptions.RemoveEmptyEntries);

        var entities = new List<int>();

        foreach (var item in users)
        {
            var entity = MigrateProvider.DbCache.ZtUsers.FirstOrDefault(n => n.Account == item);

            if (entity is not null)
            {
                entities.Add((int)entity.Id);
            }
        }

        return entities.ToArray();
    }

    private static Product? MapVirtualProduct(int value)
    {
        if (_products is null || _products.Count == 0)
        {
            _products = BgeContext.Products.ToList();
        }

        if (value is 0)
        {
            return null;
        }

        var entity = _products.FirstOrDefault(n => n.Id == value);

        return entity is not null ? entity : null;
    }

    private static Module? MapVirtualModule(int value)
    {
        if (_modules is null || _modules.Count == 0)
        {
            _modules = BgeContext.Modules.ToList();
        }

        if (value is 0)
        {
            return null;
        }

        var entity = _modules.FirstOrDefault(n => n.Id == value);

        return entity is not null ? entity : null;
    }

    private static Release? MapVirtualRelease(string value)
    {
        if (_releases is null || _releases.Count == 0)
        {
            _releases = BgeContext.Releases.ToList();
        }

        return string.IsNullOrEmpty(value)
            ? null
            : value == "trunk"
        ? new Release { Id = 0 }
        : _releases.First(n => n.Id == uint.Parse(value));
    }

    private static ICollection<Release> MapVirtualReleases(string value)
    {
        if (_releases is null || _releases.Count == 0)
        {
            _releases = BgeContext.Releases.ToList();
        }

        var releases = value.Split(',', StringSplitOptions.RemoveEmptyEntries);

        var entities = new List<Release>();

        foreach (var item in releases)
        {
            if (item == "trunk")
            {
                entities.Add(new Release { Id = 0 });
            }
            else
            {
                var entity = _releases.First(n => n.Id == uint.Parse(item));

                entities.Add(entity);
            }
        }

        return entities;
    }

    private static Bug? MapVirtualBug(int value)
    {
        return value is 0 ? null : new Bug { Id = value };
    }

    private static ICollection<Bug> MapVirtualBugs(string value)
    {
        var bugIds = value.Split(',', StringSplitOptions.RemoveEmptyEntries);

        return bugIds.Select(n => new Bug { Id = int.Parse(n) }).ToList();
    }

    #region Custom Field   
    private static BugType MapVirtualBugType(string value)
    {
        if (_bugTypes is null || _bugTypes.Count == 0)
        {
            _bugTypes = BgeContext.BugTypes.ToList();
        }

        if (string.IsNullOrEmpty(value))
        {
            return _bugTypes.First();
        }

        var lang = MigrateProvider.DbCache.ZtLangs.FirstOrDefault(n => n.Section == "typeList" && n.Module == "bug" && value.Contains(n.Key));

        if (lang is null)
        {
            return _bugTypes.First();
        }

        if (string.IsNullOrEmpty(lang.Value))
        {
            return _bugTypes.First();
        }

        var entity = _bugTypes.FirstOrDefault(n => n.Name == lang.Value);

        return entity is null ? _bugTypes.First() : entity;
    }

    private static Os MapVirtualOs(string value)
    {
        if (_oss is null || _oss.Count == 0)
        {
            _oss = BgeContext.Os.ToList();
        }

        if (string.IsNullOrEmpty(value))
        {
            return _oss.First();
        }

        var lang = MigrateProvider.DbCache.ZtLangs.First(n => n.Section == "osList" && n.Key == value);

        return string.IsNullOrEmpty(lang.Value)
            ? _oss.First()
            : _oss.First(n => n.Name == lang.Value);
    }

    private static Browser MapVirtualBrowser(string value)
    {
        if (_browsers is null || _browsers.Count == 0)
        {
            _browsers = BgeContext.Browsers.ToList();
        }

        if (string.IsNullOrEmpty(value))
        {
            return _browsers.First();
        }

        var lang = MigrateProvider.DbCache.ZtLangs.First(n => n.Section == "browserList" && n.Key == value);

        return string.IsNullOrEmpty(lang.Value)
            ? _browsers.First()
            : _browsers.First(n => n.Name == lang.Value);
    }

    private static Solution MapVirtualSolution(string value)
    {
        if (_solutions is null || _solutions.Count == 0)
        {
            _solutions = BgeContext.Solutions.ToList();
        }

        if (string.IsNullOrEmpty(value))
        {
            return _solutions.First();
        }

        var lang = MigrateProvider.DbCache.ZtLangs.First(n => n.Section == "resolutionList" && n.Key == value);

        return string.IsNullOrEmpty(lang.Value)
            ? _solutions.First()
            : _solutions.First(n => n.Name == lang.Value);
    }
    #endregion    

    private static int? MapVirtualDemand(int value)
    {
        return value is 0 ? null : value;
    }

    private static BugOperation MapVirtualOperation(string value)
    {
        return value == "opened"
            ? BugOperation.Created
            : value == "tostory"
            ? BugOperation.ToDemand
            : value == "bugconfirmed"
            ? BugOperation.Confirmed
            : value == "commented"
            ? BugOperation.Remarked
            : value == "editfile" ? BugOperation.EditedFile : Helen.ToEnum<BugOperation>(value);
    }

    private static string MapVirtualExtra(string value)
    {
        var resolution = MigrateProvider.DbCache.ZtLangs.Where(n => n.Section == "resolutionList").FirstOrDefault(n => n.Key == value);
        if (resolution is not null)
        {
            var idx = value.IndexOf(":");
            return idx > 0 ? $"{resolution.Value}:{value[idx..]}" : resolution.Value;
        }

        var user = MigrateProvider.DbCache.ZtUsers.FirstOrDefault(n => n.Account == value);

        return user is not null ? user.Realname : value;
    }

    private static string MapVirtualField(string value)
    {
        return value switch
        {
            "resolvedBuild" => "解决版本",
            "openedBuild" => "影响版本",
            "mailto" => "抄送给",
            "assignedTo" => "指派给",
            "resolvedBy" => "解决者",
            "closedBy" => "由谁关闭",
            "resolvedDate" => "解决日期",
            "closedDate" => "关闭日期",
            "activatedDate" => "激活日期",
            "deadline" => "截止日期",
            "module" => "所属模块",
            "confirmed" => "是否确认",
            "type" => "Bug类型",
            "resolution" => "解决方案",
            "status" => "Bug状态",
            "product" => "所属产品",
            "duplicateBug" => "重复Bug",
            "os" => "操作系统",
            "browser" => "浏览器",

            "title" => "标题",
            "steps" => "重现步骤",
            "severity" => "严重性",
            "pri" => "优先级",
            "activatedCount" => "激活次数",
            "fileName" => "文件名",
            "keywords" => "关键字",
            "toStory" => "转为需求",
            "linkBug" => "关联Bug",
            _ => string.Empty,
        };
    }

    private static string MapVirtualRecord(ZtHistory source, bool isOldField)
    {
        var value = isOldField ? source.Old : source.New;

        if (source.Field == "resolvedBuild")
        {
            if (value == "trunk")
            {
                return "主干";
            }

            if (string.IsNullOrEmpty(value))
            {
                return string.Empty;
            }

            var entity = MigrateProvider.DbCache.ZtReleases.FirstOrDefault(n => n.Id == uint.Parse(value));

            return entity is not null ? entity.Name : string.Empty;
        }

        if (source.Field == "openedBuild")
        {
            var builds = value.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(n =>
            {
                if (n == "trunk")
                {
                    return "主干";
                }

                if (string.IsNullOrEmpty(n))
                {
                    return string.Empty;
                }

                var entity = MigrateProvider.DbCache.ZtReleases.FirstOrDefault(x => x.Id == uint.Parse(n));

                return entity is not null ? entity.Name : string.Empty;
            }).ToList();

            builds.RemoveAll(n => string.IsNullOrEmpty(n));

            return string.Join("，", builds);
        }

        if (source.Field == "mailto")
        {
            var users = value.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(n =>
            {
                var entity = MigrateProvider.DbCache.ZtUsers.FirstOrDefault(n => n.Account == value);

                return entity is not null ? entity.Realname : string.Empty;
            }).ToList();

            users.RemoveAll(n => string.IsNullOrEmpty(n));

            return string.Join("，", users);
        }

        if (source.Field is "assignedTo" or
            "resolvedBy" or
            "closedBy")
        {
            var entity = MigrateProvider.DbCache.ZtUsers.FirstOrDefault(n => n.Account == value);

            return entity is not null ? entity.Realname : string.Empty;
        }

        if (source.Field is "resolvedDate" or
            "closedDate" or
            "activatedDate" or
            "deadline")
        {
            return DateTime.TryParse(value, out var date) ? date.ToString() : string.Empty;
        }

        if (source.Field == "module")
        {
            var entity = MigrateProvider.DbCache.ZtModules.FirstOrDefault(n => n.Id == uint.Parse(value));

            return entity is not null ? entity.Name : string.Empty;
        }

        if (source.Field == "type")
        {
            var entity = MigrateProvider.DbCache.ZtLangs.FirstOrDefault(n => n.Section == "tpyeList" && value.Contains(n.Key));

            return entity is not null ? entity.Value : string.Empty;
        }

        if (source.Field == "resolution")
        {
            var entity = MigrateProvider.DbCache.ZtLangs.FirstOrDefault(n => n.Section == "resolutionList" && n.Key == value);

            return entity is not null ? entity.Value : string.Empty;
        }

        if (source.Field == "status")
        {
            var target = Helen.GetEnumDisplayValue(Helen.ToEnum<BugStatus>(value));

            return target;
        }

        if (source.Field == "product")
        {
            var entity = MigrateProvider.DbCache.ZtProducts.FirstOrDefault(n => n.Id == uint.Parse(value));

            return entity is not null ? entity.Name : string.Empty;
        }

        if (source.Field == "os")
        {
            var entity = MigrateProvider.DbCache.ZtLangs.FirstOrDefault(n => n.Section == "osList" && n.Key == value);

            return entity is not null ? entity.Value : string.Empty;
        }

        if (source.Field == "browser")
        {
            var entity = MigrateProvider.DbCache.ZtLangs.FirstOrDefault(n => n.Section == "browserList" && n.Key == value);

            return entity is not null ? entity.Value : string.Empty;
        }

        return source.Field == "confirmed" ? value == "1" ? "是" : "否" : value;
    }

    #region 迁移缓存
    private static List<BugType> _bugTypes = null!;
    private static List<Os> _oss = null!;
    private static List<Browser> _browsers = null!;
    private static List<Solution> _solutions = null!;
    private static List<Product> _products = null!;
    private static List<Module> _modules = null!;
    private static List<Release> _releases = null!;

    public static async ValueTask DisposeAsync()
    {
        await BgeContext.DisposeAsync();

        _bugTypes?.Clear();
        _bugTypes = null!;

        _oss?.Clear();
        _oss = null!;

        _browsers?.Clear();
        _browsers = null!;

        _solutions?.Clear();
        _solutions = null!;

        _products?.Clear();
        _products = null!;

        _modules?.Clear();
        _modules = null!;

        _releases?.Clear();
        _releases = null!;
    }
    #endregion
}